#include <memory.h>
#include "service.h"
#include "service_handler.h"
#include "service_timer.h"
extern "C" {
#include <event2/event.h>
#include <event.h>
#include <event2/listener.h>
#include "event2/thread.h"
};

using namespace std;
enum {
	SERVICE_CMD_STOP,
	SERVICE_CMD_CLIENTCMD
};

struct ServiceClientCommand {
	string cmd;
	void* data;
};

struct ServiceCommand {
	int type;
	void* data;
};

Service::Service(void)
{
	m_handler = NULL;
	evutil_socketpair(AF_UNIX, SOCK_STREAM, 0, m_sockets);
	m_base = event_base_new();
	evutil_make_socket_nonblocking(m_sockets[0]);
	m_bevRecv = bufferevent_socket_new(m_base, m_sockets[0], BEV_OPT_DEFER_CALLBACKS);
	bufferevent_enable(m_bevRecv, EV_READ);
	bufferevent_setcb(m_bevRecv, onChannelRecv, NULL, NULL, this);
	m_running = true;
	
}


void* Service::doWork( void* data )
{
	Service* service = (Service*)data;
	event_base_dispatch(service->m_base);

	event_base_free(service->m_base);
	service->m_base = NULL;
	service->m_running = false;
	return 0;
}

bool Service::start() 
{
	m_thread = new thread(doWork, this);
	return true;
}

void Service::onChannelRecv( bufferevent* bev, void* data ) 
{
	Service* service = (Service*)data;

	while(evbuffer_get_length(bev->input) >= sizeof(ServiceCommand)) {
		ServiceCommand cmd;
		bufferevent_read(bev, &cmd, sizeof(ServiceCommand));
		
		if(!service->procCommand(cmd.type, cmd.data)) {
			break;
		}
	}
}

bool Service::procCommand( int type, void* data )
{
	switch(type) {
	case SERVICE_CMD_STOP:	
		{
			evutil_closesocket(m_sockets[0]);
			evutil_closesocket(m_sockets[1]);
			bufferevent_free(m_bevRecv);
			memset(m_sockets, 0, sizeof(m_sockets));
			m_bevRecv = NULL;
		}
		return false;
	case SERVICE_CMD_CLIENTCMD:
		{
			ServiceClientCommand* cmd = (ServiceClientCommand*)data;
			if(m_handler) {
				m_handler->handleCommand(cmd->cmd, cmd->data);
			}
			delete cmd;
		}
		return true;
	default:
		return true;
	}

}

void Service::stop() 
{
	ServiceCommand cmd;
	cmd.type = SERVICE_CMD_STOP;
	cmd.data = NULL;
	m_mutex.lock();
	send(m_sockets[1], (char*)&cmd, sizeof(ServiceCommand), 0);
	m_mutex.unlock();
	if (m_thread->joinable()) {
		m_thread->join();
	}
	
	delete m_thread;
	m_thread = NULL;
}

event_base* Service::getBase()
{
	return m_base;
}

void Service::setHandler( ServiceHandler* handler )
{
	m_handler = handler;
}

ServiceTimer * Service::createTimer(int timeout, ServiceTimerHandler * timerHandler, bool repid)
{
	ServiceTimer* timer = new ServiceTimer(this, timerHandler, timeout, repid);
	return timer;
}

void Service::sendCommand( std::string cmd, void* data )
{
	ServiceClientCommand* clientCmd = new ServiceClientCommand;
	clientCmd->cmd = cmd;
	clientCmd->data = data;
	ServiceCommand serviceCommand;

	serviceCommand.type = SERVICE_CMD_CLIENTCMD;
	serviceCommand.data = clientCmd;
	m_mutex.lock();
	send(m_sockets[1], (char*)&serviceCommand, sizeof(ServiceCommand), 0);
	m_mutex.unlock();
}
